#include"eventpool.hh"
#include"unistd.h"
#include<sys/eventfd.h>

Eventpool::Eventpool(Acceptor& rhs)
    :_epfd(createepfd())
     ,_isloop(true)
     ,_evfd(createvfd())
     ,_acceptor(rhs)
{
    add_epoll(rhs.fd());
    add_epoll(_evfd);
}
    Eventpool::~Eventpool()
{
    close(_epfd);
}

    int Eventpool::createepfd()
{
    int epfd=epoll_create(0);
    if(epfd==-1)
    {
        perror("createepoll");
    }
    return epfd;
}

    void Eventpool::loop()
{
    _isloop=true;
    while(_isloop)
    {
        wait();
    }
}

    void Eventpool::wait()
{
    int ret=0;
    do
    {
    ret=epoll_wait(_epfd,&*_list.begin(),_list.size(),3000);
    }while(ret ==-1 && errno==EINTR);

    if(ret ==-1)
    {
        perror(" wait_failed");
        return;
    }
    else if(ret==0)
    {
        perror("have waitting for 3000ms");
    }
    else
    {
        if(ret ==(int)_list.size())
        {
            _list.resize(2*ret);
        }
        for(int i=0;i<ret;++i)
        {
            int fd =_acceptor.fd();
            int ef=_list[i].data.fd;
            if(fd==ef)
            {
                handleNewconnect();
            }
            else if(ef==_evfd)
            {
               handleread(); 
               dodtask();
            }
            else
            {
                handleNewmsg(fd);
            }
        }

    }

}

    void Eventpool::unloop()
{
    _isloop=false;
}

    void Eventpool::handleNewmsg(int fd)
{
    //处理新信息
    auto it = _tcp.find(fd);
    if(it==_tcp.end())
    {
        perror("msg fd wrong ");
        return ;
    }
    else
    {
        bool check= it->second->is_closed();
        if(check)
        {
            _tcp[fd]->handleClosecall();
            del_epoll(fd);
            _tcp.erase(it);
        }
        else
        {
            _tcp[fd]->handleMsgcall();
        }


        }
    _tcp[fd]->handleMsgcall();

}
    void Eventpool::handleNewconnect()
{
    int netfd=_acceptor.accept();
    if(netfd<0)
    {
        perror(" accepte wrong in  Eventpool");
        return;
    }
    add_epoll(netfd);
    Tcpconptr pt(new TcpConnection(netfd));
    pt->SetCloseCallback(std::move(_onClose));
    pt->SetNewconnCallback(std::move(_onNewcon));
    pt->SetNewMsgCallback(std::move(_onMsg));
    _tcp[netfd]=pt;
    pt->handleConncall();
}


    void Eventpool::add_epoll(int fd)
{
    struct epoll_event event;
    event.events=EPOLLIN;
    event.data.fd=fd;

    int ret=epoll_ctl(_epfd,EPOLL_CTL_ADD,fd,&event);
    if(ret<0)
    {
        perror(" add_epoll wrong");
        return;
    }
}
    void Eventpool::del_epoll(int fd)
{

    struct epoll_event event;
    event.events=EPOLLIN;
    event.data.fd=fd;
    int ret=epoll_ctl(_epfd,EPOLL_CTL_DEL,fd,&event);
    if(ret<0)
    {
        perror(" add_epoll wrong");
        return;
    }
}

    void Eventpool::SetCloseCallback(func_call&& rhs)
{

    _onClose=rhs;

}

    void Eventpool::SetNewconnCallback(func_call&& rhs)
{
    _onNewcon=rhs;

}

    void Eventpool::SetNewMsgCallback(func_call&& rhs)
{
    _onMsg=rhs;
}

    int  Eventpool::createvfd(){
        int fd =eventfd(10,0);
        if(fd<0)
        {
            perror(" event fd wrong");
            return fd;
        }
        return fd;
    }

    void Eventpool::handleread(){
        uint64_t one =1;
        int ret =read(_evfd,&one,sizeof(uint64_t));
        if(ret != sizeof(uint64_t))
        {
            perror(" handleread ");
            return;
        }

    }

    void Eventpool::wakup(){
        uint64_t one =1;
        int ret =write(_evfd,&one,sizeof(uint64_t));
        if(ret != sizeof(uint64_t))
        {
            perror(" wakeup ");
            return;
        }
    }

    void Eventpool::runInLoop(func &&rhs){
        {
            std::unique_lock<std::mutex> lock(_mut);
        _task.push_back(rhs);
        }
        wakup();
    }

    void Eventpool::dodtask(){
        std::vector<func> tmp;
        {
            std::unique_lock<std::mutex> lock(_mut);
            tmp.swap(_task);
        }
        for(auto & i: tmp)
        {
            i();
        }
    }

